home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / prolog / ai.prl / mike1.exe / MINIMIKE.PL < prev    next >
Encoding:
Text File  |  1990-07-14  |  13.2 KB  |  250 lines

  1. /* MINIMIKE.PL  ('How to run it' instructions at end of file)
  2.  
  3. 'Minimalist' MIKE interpreter as described in the BYTE Magazine article
  4. "Build your own knowledge engineering toolkit" by Eisenstadt & Brayshaw.
  5. The 'bells and whistles' full version of MIKE is in MIKE.PL and its
  6. associated files.
  7.  
  8. Copyright (c) 1990 The Open University (U.K.)
  9.  
  10. The Open University accepts no responsibility for any legal or other
  11. consequences which may arise directly or indirectly as a result of the
  12. use of all or parts of the contents of this program.
  13.  
  14.  
  15. */
  16.  
  17. ?- op(1200,fx,rule).
  18. ?- op(1199,xfx,with).
  19. ?- op(1199,xfx,forward).
  20. ?- op(1199,xfx,backward).
  21. ?- op(1100,fx,if).
  22. ?- op(1000,xfx,then).
  23. ?- op(1000,xfx,from).
  24. ?- op(999, fx,make_value).
  25. ?- op(999, fx,add_value).
  26. ?- op(950,fx, '--').
  27. ?- op(950,fx, establish).
  28. ?- op(950,fx, deduce).
  29. ?- op(950,fx, say).
  30. ?- op(950,fx, remove).
  31. ?- op(950,fx, note).
  32. ?- op(950,fx, add).
  33. ?- op(950,fx, announce).
  34. ?- op(950,xfy,explained_by).
  35. ?- op(950,fx,why).
  36. ?- op(950,fx,how).
  37. ?- op(950,fx,describe).
  38. ?- op(950,fx,show).
  39. ?- op(950,fx,strategy).
  40. ?- op(955,xfy, or).
  41. ?- op(954, xfy, '&').
  42. ?- op(953,fx, query).
  43. ?- op(952,xfy, receives_answer).
  44. ?- op(899,fx,the).
  45. ?- op(899,fx,all).
  46. ?- op(898,xfx,of).
  47. ?- op(897,xfx,to).
  48. ?- op(876,xfx,for).
  49. ?- op(850,xfx,are).
  50. ?- op(800,xfx,instance_of).
  51. ?- op(800,xfx,subclass_of).
  52. ?- op(799,xfx,':').
  53. ?- op(200,xfx,'<--').
  54. ?- op(10,fx,'?').
  55.  
  56. /* Inheritance is a recursive search along 'subclass_of' relations:
  57. Here is the relevant Prolog code, assuming our operators have already been
  58. defined.  The first clause converts the surface form into our internal
  59. form.  The two clauses of fetch cater for the cases in which the object is
  60. stored either as an instance_ of something or else when it is stored as a
  61. subclass_of something. */
  62.  
  63. the Attribute of Object is Value :-    /*surface form for user*/
  64.   fetch(Object, Attribute, Value).   /* our internal form*/
  65. fetch(Object, Attribute, Value) :-   /* here's its definition....*/
  66.   (Object instance_of SuperObject with Stuff),   /* get stored frame*/
  67.   retrieve(Object, Attribute, Value, SuperObject, Stuff).  /* invoke real workhose*/
  68. fetch(Object, Attribute, Value) :-   /* alternatively....*/
  69.   (Object subclass_of Class with Stuff),   /* stored frame might be found here*/
  70.   retrieve(Object, Attribute, Value, Class, Stuff).  /* so invoke real workhorse*/
  71. retrieve(Obj, Attr, Val, Super, (Attr:Val)) :-   /* Direct hit (single slot:filler)*/
  72.   not(Val = [_ | _]).   /* assumes singleton value, not a list*/
  73. retrieve(Obj, Attr, Val, Super, (Attr:Val, Rest)):-  /* Direct hit (first slot:filler pair of many)*/
  74.   not(Val = [_ | _]).   /* assumes singleton value, not a list*/
  75. retrieve(Obj, Attr, Val, Super, (Attr:Vals)) :-   /* Single slot with filler which is a list*/
  76.   member(Val, Vals).   /* so see that Val is on list of Vals*/
  77. retrieve(Obj, Attr, Val, Super, (Attr:Vals, Rest)) :-  /* First pair of many, filler is a list...*/
  78.   member(Val, Vals).   /* so see that Val is on list of Vals*/
  79. retrieve(Obj, Attr, Val, Super, (_:_, Rest)) :-   /* last arg is nasty conjunct....*/
  80.   retrieve(Obj, Attr, Val, Super, Rest). /* so traverse it searching for direct hit*/
  81. retrieve(Obj, Attr, Val, Super, _) :-   /* direct hits must have failed, so...*/
  82.   fetch(Super, Attr, Val).   /* recursively check out the superset!!*/
  83. /*
  84. The real work is done by the clauses of retrieve, and in particular by its
  85. final argument.  The first clause of retrieve represents the case where the
  86. slot-filler pair Attr:Val just happens (a) to be the only slot-filler pair,
  87. (b) has a filler which is not a list, i.e. does not syntactically match the
  88. form [_|_], and (c) is a successful match with the slot:filler pair we are
  89. searching for.  The second clause is similar, but in this case the slot-
  90. filler pair Attr:Val is the very first pair in the (possibly long)
  91. conjunction of many.  The third and fourth clauses are analogous to the
  92. first and second, but cater for the case when the filler is a list of
  93. values such as [teacher, lifeguard, parent], and therefore it is necessary
  94. to invoke member to see whether Val is a member of the list of Vals.  The
  95. fifth clause of retrieve optimistically tries to do more of the same, but
  96. this time matching against Rest, i.e. all but the very first of the slot-
  97. filler pairs.  This is a standard clichΘ in P rolog, used for traversing
  98. lists or conjunctions of items. The final clause of retrieve is only
  99. reached when the first five have failed. It invokes fetch, but this time
  100. passing in Super as the first argument to fetch, so that the searching
  101. activity begins with the superordinate object in the class hierarchy (e.g.
  102. person, in the case of fred_smith).  This caters for the cases when the
  103. slot-filler pair is not retrievable for a given object, so an attempt is
  104. made to retrieve the information further up the chain-- this is the kernel
  105. of what is meant by 'inheritance'.  There are some important details which
  106. are omitted here, especially the problem of what to do when there is a
  107. conflict between 'directly stored' slot-filler pairs and 'inherited' slot-
  108. filler pairs.  MIKE handles this correctly (e.g. knowledge that an ostrich
  109. cannot fly overrides the knowledge that birds can fly), as illustrated in
  110. the commented MIKE source code which is availabe in MIKE.PL and its
  111. associated files (especially ENGINE.PL and FC_EXEC.PL).    */
  112.  
  113. /* Backward chaining works just like Prolog itself:  The implementation of
  114. backward chaining is straightforward, because it merely requires an
  115. invocation of the basic Prolog proof procedure.  There are four main cases
  116. to deal with:
  117.  conjunction of goals (e.g. 'it is raining' & 'it is cold' &
  118. 'it is Tuesday'):  the technique is to invoke the proof procedure
  119. recursively on the the first conjunct, and then on the remaining conjuncts.
  120.  frame access: (e.g. the age of john is 32): the technique is to invoke
  121. the workhorse predicate fetch, which was defined in the preceding section
  122.  ordinary working memory element (e.g. 'it is raining'): working memory
  123. elements such as 'it is raining' are stored internally using the predicate
  124. wm, so we just need to see whether wm(<pattern>) succeeds.
  125.  conclusion of a rule: the technique is to find a stored rule which matches
  126. the conclusion, and then recursively prove the premises of that rule.
  127. These four cases map precisely onto the four clauses of prove shown below: */
  128.  
  129. prove(First & Rest) :-   /* conjunction of goals...*/
  130.   prove(First),   /* ... so prove the first one*/
  131.   prove(Rest).   /* ... and then prove the rest*/
  132. prove(the Attribute of Object is Value) :-   /* frame access...*/
  133.   fetch(Object, Attribute, Value).   /* ... so invoke frame access workhorse*/
  134. prove(Pattern) :-   /* a pattern is 'satisfied'....*/
  135.   wm(Pattern).   /* ... if it is stored in 'working memory'*/
  136. prove(Conclusion) :-   /* a conclusion can be proved...*/
  137.   (rule R backward if Premises then Conclusion),   /* ... by retrieving a rule in which it appears...*/
  138.   prove(Premises).   /* ... and then proving that rule's premises*/
  139.  
  140. /* Forward chaining searches for the first rule which has all of its
  141. conditions already 'satisfied':
  142. Forward chaining represents 'opportunistic' processing (in contrast to
  143. goal-directed processing).  The basic processing technique is to find any
  144. rule, all of whose left-hand-side patterns (premises) are 'satisfied' (i.e.
  145. in working memory), and then perform the associated right-hand-side actions
  146. of that rule.  Having done that, the next thing is to do more forward
  147. chaining.  Successful termination occurs when the symbol halt is placed
  148. into working memory.  The next three clauses capture this pro cessing
  149. concept concisely, with the final clause merely representing the
  150. terminating condition, when no further suitable rules can be found: */
  151.  
  152. forward_chain :- /* deliberate termination occurs if... */
  153.   wm(halt),      /* the symbol 'halt' is added to working memory */
  154.   nl,
  155.   write('Successful termination.'),
  156.   nl.            /* ... so inform user accordingly */
  157.  
  158. forward_chain :-
  159.   (rule RuleName forward if LHS then RHS), /* find a rule.... */
  160.   all_in_mem(LHS), /* whose left-hand-side patterns are all satisfied...*/
  161.   not(already_did(RuleName,LHS)), /* and which we haven't already performed */
  162.   perform(RHS), /* then perform associated right-hand-side actions */
  163.   assert(already_did(RuleName,LHS)),   /*avoid repeating this exact case */
  164.   forward_chain.   /* then carry on forward-chaining */
  165. forward_chain :-   /* this case only reached when above clause fails */
  166.   nl,
  167.   write('No (more) applicable rules.'),
  168.   nl.  /* ... so inform user accordingly */
  169.  
  170.  
  171.  
  172. /* A top-level goal fc ensures that working memory is cleared up prior to execution, and places the special symbol 'start' in working memory before invoking the workhorse forward_chain:
  173. */
  174. fc :-     /* top-level invocation*/
  175.   abolish(wm, 1),   /* clear out working memory*/
  176.   assert(wm(start)),   /* add special 'start' symbol to working memory*/
  177.   abolish(already_did, 2),   /* clear flag which prevents duplicate firings*/
  178.   forward_chain.   /* invoke forward_chain workhorse*/
  179.  
  180. /*
  181. During forward chaining, a rule's left-hand-side pattern is said to be
  182. 'satisfied' either by being present in working memory or by being
  183. retrievable from frame memory.  Working memory elements are stored
  184. internally using the predicate wm, so in the m ost general case we just
  185. need to see whether wm(<pattern>) succeeds.  More special cases exist, for
  186. dealing with patterns such as the X of Y is Z, so the first four clauses of
  187. in_mem cater with these cases, while the general case is left for last. */
  188.  
  189. all_in_mem(First & Rest) :-    /* conjunction of left-hand-side patterns*/
  190.   in_mem(First),   /* see if the first one is satisfied*/
  191.   all_in_mem(Rest).   /* recursively see if rest are satisfied*/
  192. all_in_mem(X) :-   /* singleton pattern*/
  193.   not(X = (_ & _)),   /* this ensures that it really is just a singleton, not a conjunction*/
  194.   in_mem(X).   /* see if it is stored in working memory  or frame memory*/
  195. in_mem(the Attr of Obj is Val) :-    /* patterns of this form require frame access..*/
  196.   fetch(Obj, Attr, Val).   /* ... so invoke the frame-retrieval workhorse*/
  197. in_mem(X instance_of Y) :-    /* this is useful for looking up instance_of relations...*/
  198.   (X instance_of Y with _ ).   /* in which case we just ignore the details following 'with'*/
  199. in_mem(X subclass_of Y) :-    /* this is useful for looking up subclass_of  relations...*/
  200.   (X subclass_of Y with _).   /* in which case we just ignore the details following 'with' */
  201. in_mem(deduce X) :-    /* this is how we invoke a backward-chaining rule...*/
  202.   prove(X).   /* ...in which case we let the workhorse  prove do the work*/
  203. in_mem(X) :-   /* this is the usual case, i.e. looking for an arbitrary pattern...*/
  204.   wm(X).   /* just see if it is in the Prolog database in this form.*/
  205. /*
  206. In a 'pure' production system interpreter, the concept of performing right-hand-side actions is restricted to adding or removing elements from working memory.  In MIKE, we make this explicit with the operators add and remove, and allow other special
  207. actions as well, such as announce and halt.  The first clause below handles
  208. conjunctions of right hand side elements, while the second and third
  209. clauses deal respectively with adding and removing working memory elements.
  210. The fourth clause caters for cosmetic printout routines, and the final
  211. clause (the default case) adds the special symbol halt to working memory
  212. for the benefit of the forward_chain workhorse routine. */
  213.  
  214. perform(First & Rest) :-   /* conjunction of right-hand-side patterns*/
  215.   perform(First),   /* do the first one (this will involve one of the clauses below)*/
  216.   perform(Rest).   /* and then do the rest*/
  217. perform(add X) :-   /* MIKE operator 'add' signifies 'add WM pattern'*/
  218.   assert(wm(X)).   /* invoke Prolog's assert, which stores pattern in database*/
  219. perform(remove X) :-   /* MIKE operator 'remove' signifies 'remove WM pattern'*/
  220.   retract(wm(X)).   /* invoke Prolog's retract, which erases pattern from database*/
  221. perform(announce X) :-   /* cosmetic printout, e.g. announce ['hi there,', X]*/
  222.   writel(X).   /* invoke user-defined Prolog utility to perform printout*/
  223. perform(halt) :-   /* special trap for 'halt' action*/
  224.   assert(wm(halt)).   /* just add pattern for forward_chain to notice.*/
  225.  
  226. /* AUXILLIARY DEFINITION NEEDED TO RUN SIMPLE EXAMPLES */
  227. /* writel  writes out a list of terms (this is invoked by 'announce') */
  228.  
  229. writel([]) :-     /* end of list to print? */
  230.   nl.             /* output a 'new line' */
  231. writel([nl|Rest]) :-  /* special symbol 'nl' included in list? */
  232.   nl,             /* output a 'new line' */
  233.   writel(Rest).   /* recursively do the rest */
  234. writel([X|Xs]) :- /* typical list */
  235.   write(X),       /* write out first element */
  236.   writel(Xs).     /* recursively do the rest */
  237.  
  238. /* ====================  HOW TO RUN IT ================================
  239.  
  240. Try out the following example:
  241.  
  242.       STEP                        WHAT TO DO
  243.  
  244. 1. INVOKE PROLOG                 C:> prolog
  245. 2. LOAD MINIMIKE                 ?- reconsult('minimike.pl').
  246. 3. LOAD SIMPLE KB                ?- reconsult('flu.kb').
  247. 4. INVOKE FORWARD CHAINING       ?- fc.
  248. 5. INSPECT WORKING MEMORY        ?- listing(wm).
  249.  
  250. */